{% macro field_from_json(field_expr, type, field_required) %}
    {%- if type.is_datetime() -%}
        {% if not field_required -%}
            {{ field_expr }} ? new Date({{ field_expr }}) : null
        {%- else -%}
            new Date({{ field_expr }})
        {%- endif -%}
    {%- elif type.is_schema_ref() -%}
        {# TODO(10055) remove this hack #}
        {% if type.to_js() == "any" -%}
            {{ field_expr }}
        {% else -%}
            {%- if not field_required -%}
                {{ field_expr }} ? {{ type.to_js() }}Serializer._fromJsonObject({{ field_expr }}): undefined
            {%- else -%}
                {{ type.to_js() }}Serializer._fromJsonObject({{ field_expr }})
            {%- endif %}
        {% endif -%}
    {%- elif type.is_list() or type.is_set() -%}
        {{ field_expr }}
        {%- set inner_t = type.inner_type() -%}
        {%- if inner_t.is_datetime()
            or inner_t.is_schema_ref()
            or inner_t.is_list()
            or inner_t.is_set()
            or inner_t.is_map() -%}
            {% if not field_required -%}?{% endif -%}
            .map((item: {{ inner_t.to_js() }}) => {{ field_from_json("item", inner_t, true) }})
        {%- endif -%}
    {%- elif type.is_map() -%}
        {%- set value_t = type.value_type() -%}
        {%- if value_t.is_datetime()
            or value_t.is_schema_ref()
            or value_t.is_list()
            or value_t.is_set()
            or value_t.is_map() -%}
            Object.fromEntries(Object.entries<{{ value_t.to_js() }}>({{ field_expr }}).map(
                (item : [string, {{ value_t.to_js() }}]) => [item[0], {{ field_from_json("item[1]", value_t, true) }}]
            ))
        {%- else -%}
            {{ field_expr }}
        {%- endif -%}
    {%- else -%}
        {{ field_expr }}
    {%- endif -%}
{% endmacro -%}

{% macro field_to_json(field_expr, type, field_required) %}
    {%- if type.is_schema_ref() -%}
        {# TODO(10055) remove this hack #}
        {% if type.to_js() == "any" -%}
            {{ field_expr }}
        {% else -%}
            {%- if not field_required -%}
            {{ field_expr }} ? {{ type.to_js() }}Serializer._toJsonObject({{ field_expr }}) : undefined
            {%- else -%}
            {{ type.to_js() }}Serializer._toJsonObject({{ field_expr }})
            {%- endif -%}
        {% endif -%}
    {%- elif type.is_list() or type.is_set() -%}
        {{ field_expr }}
        {%- set inner_t = type.inner_type() -%}
        {%- if inner_t.is_schema_ref()
            or inner_t.is_list()
            or inner_t.is_set()
            or inner_t.is_map() -%}
            {%- if not field_required -%}?{% endif -%}
            .map((item) => {{ field_to_json("item", inner_t, true) }})
        {%- endif -%}
    {%- elif type.is_map() -%}
        {%- set value_t = type.value_type() -%}
        {%- if value_t.is_schema_ref()
            or value_t.is_list()
            or value_t.is_set()
            or value_t.is_map() -%}
            Object.fromEntries(Object.entries({{ field_expr }}).map(
                (item) => [item[0], {{ field_to_json("item[1]", value_t, true) }}]
            ))
        {%- else -%}
            {{ field_expr }}
        {%- endif -%}
    {%- else -%}
        {{ field_expr }}
    {%- endif -%}
{% endmacro -%}
